//---------------------------------------------------------------------------
/**
 * \class CSpace
 * \brief This is a space class to by store (key,value) pairs.
 * \version 0.1
 *
 * \author Woo-Hyun Kim (woorung@nhncorp.com, woorung@gmail.com)
 * \author http://blog.naver.com/wisereign .
 *
 * \date 2007/09/04
 */
//---------------------------------------------------------------------------
#include "CSpace.h"

CSpace::CSpace(char *name)
{
	pthread_mutex_init(&space_mutex, NULL);
	pthread_cond_init(&space_cond, NULL);

	this->name = name;
}

CSpace::~CSpace()
{
	space.clear();
	
	pthread_mutex_destroy(&space_mutex);
	pthread_cond_destroy(&space_cond);
}

int CSpace::clear()
{
	pthread_mutex_lock(&space_mutex);

	space.clear();

	pthread_mutex_unlock(&space_mutex);

	return size();
}

int CSpace::write(CObject *obj) 
{
	return write(NULL, obj, MAX_TTL);
}

int CSpace::write(CObject *obj, time_t ttl) 
{
	return write(NULL, obj, ttl);
}

int CSpace::write(CObject *key, CObject *obj) 
{
	return write(key, obj, MAX_TTL);
}

int CSpace::write(CObject *key, CObject *obj, time_t ttl)
{
	if(ttl!=MAX_TTL) ttl += time(NULL);

        pthread_mutex_lock(&space_mutex);

	if(key==NULL||space.empty()) { // just add
		space.push_back(*obj);
		CObject &that = space.back(); // back() returns reference
		that.setTTL(ttl);
	}
	else {
		bool found = false;
		for(int i=0;i<space.size();i++)  {
			if(*key==*(space[i])) { // real instance comparison
				space[i] = *obj; // smart template(copy, assign)
				CObject *p = space[i];
				p->setTTL(ttl);

				found = true;
				break;
			}
		}

		if(!found) {
			space.push_back(*obj);
			CObject &that = space.back(); // back() returns reference
			that.setTTL(ttl);
		}
	}

	int size = space.size();;
	// ----------------------------------

	// signal to pthread_cond_wait()
        //pthread_cond_signal(&space_cond);
        pthread_cond_broadcast(&space_cond);

        pthread_mutex_unlock(&space_mutex);

        return size;
}

CObject *CSpace::read(CObject *obj)
{
	return read(NULL, obj);
}

CObject *CSpace::read(CObject *key, CObject *obj)
{
	obj = NULL;

        pthread_mutex_lock(&space_mutex);

		// wait until being called by pthread_cond_signal()
		while(space.empty()) pthread_cond_wait(&space_cond, &space_mutex);

		if(key==NULL) { // just read from front
			CObject &ref_obj = space.front(); // front() returns reference
			obj = ref_obj.clone();
		}
		else {
			for(int i=0;i<space.size();i++)  {
				if(*key==*(space[i])) { // real instance comparison
					CObject *found_obj = space[i];
					obj = found_obj->clone(); // return new allocated object 

					break;
				}
			}
		}

        pthread_mutex_unlock(&space_mutex);

        return obj;
}

// !!! Attention!!!
// # in case of bulk read, the position to read should move to the next
vector< CSmartTemplate<CObject> > &CSpace::read(CObject *key, size_t read_limit, vector< CSmartTemplate<CObject> > &objs)
{
        pthread_mutex_lock(&space_mutex);

	// wait until being called by pthread_cond_signal()
	while(space.empty()) pthread_cond_wait(&space_cond, &space_mutex);

	if(key==NULL) { // just read all objects
		for(int limit=0;limit<read_limit;limit++) {
			if(space.empty()) break;
			objs.push_back(space[limit]);
		}
	}
	else {
		for(int limit=0, i=0;i<read_limit;i++) {
			if(space.empty()) break;
			if(*key==*(space[i])) {
				objs.push_back(space[i]);

				limit++;
			}
		}
	}

        pthread_mutex_unlock(&space_mutex);

        return objs;
}

CObject *CSpace::readIfExists(CObject *obj)
{
	return readIfExists(NULL, obj);
}

CObject *CSpace::readIfExists(CObject *key, CObject *obj)
{
	obj = NULL;

        pthread_mutex_lock(&space_mutex);

	// ----------------------------------
	// return if there is no object 
	if(!space.empty()) {
		if(key==NULL) { // just read from front
			CObject &ref_obj = space.front(); // front() returns reference
			obj = ref_obj.clone();
		}
		else {
			for(int i=0;i<space.size();i++)  {
				if(*key==*(space[i])) { // real instance comparison
					CObject *found_obj = space[i];
					obj = found_obj->clone(); // return new allocated object 
					break;
				}
			}
		}
	}
	// ----------------------------------

        pthread_mutex_unlock(&space_mutex);

        return obj;
}

// !!! Attention!!!
// # in case of bulk readIfExists, the position to read should move to the next
vector< CSmartTemplate<CObject> > &CSpace::readIfExists(CObject *key, size_t read_limit, vector< CSmartTemplate<CObject> > &objs)
{
        pthread_mutex_lock(&space_mutex);

	int limit = max(read_limit, space.size());

	// return if there is no object 
	if(!space.empty()) {
		if(key==NULL) { // just read all objects
			for(int limit=0;limit<read_limit;limit++) {
				if(space.empty()) break;
				objs.push_back(space[limit]);
			}
		}
		else {
			for(int limit=0, i=0;i<read_limit;i++) {
				if(space.empty()) break;
				if(*key==*(space[i])) {
					objs.push_back(space[i]);

					limit++;
				}
			}
		}
	}

        pthread_mutex_unlock(&space_mutex);

        return objs;
}

CObject *CSpace::take(CObject *obj)
{
	return take(NULL, obj);
}

CObject *CSpace::take(CObject *key, CObject *obj)
{
	obj = NULL;

        pthread_mutex_lock(&space_mutex);

	// wait until being called by pthread_cond_signal()
	while(space.empty()) pthread_cond_wait(&space_cond, &space_mutex);

	if(key==NULL) { // just read from front
		CObject &ref_obj = space.front(); // front() returns reference
		obj = ref_obj.clone();
		space.pop_front(); // erase the front object
	}
	else {
		for(int i=0;i<space.size();i++)  {
			if(*key==*(space[i])) { // real instance comparison
				CObject *found_obj = space[i];
				obj = found_obj->clone(); // return new allocated object 
				// erase the found object
				space.erase(space.begin()+i);

				break;
			}
		}
	}

        pthread_mutex_unlock(&space_mutex);

        return obj;
}

vector< CSmartTemplate<CObject> > &CSpace::take(CObject *key, size_t take_limit, vector< CSmartTemplate<CObject> > &objs)
{
	for(int limit=0;limit<take_limit;limit++) {
		CObject *obj = NULL;
		obj = take(key, obj);
		if(obj==NULL) break;
		objs.push_back(*obj);
		delete obj;
	}

/*
        pthread_mutex_lock(&space_mutex);

		// wait until being called by pthread_cond_signal()
		while(space.empty()) pthread_cond_wait(&space_cond, &space_mutex);

		if(key==NULL) { // just take bulk objects
			for(int limit=0;limit<take_limit;limit++) {
				if(space.empty()) break;
				CObject &ref_obj = space.front(); // front() returns reference
				objs.push_back(ref_obj);
				space.pop_front(); // erase the front object
			}
		}
		else {
			int limit = 0;
			for(int i=0;i<space.size();i++)  {
				if(*key==*(space[i])) { // real instance comparison
					objs.push_back(space[i]);
					// erase the found object
					space.erase(space.begin()+i);

					limit++;
					if(limit>=take_limit) break;
				}
			}
		}

        pthread_mutex_unlock(&space_mutex);
*/

        return objs;
}

CObject *CSpace::takeIfExists(CObject *obj)
{
	return takeIfExists(NULL, obj);
}

CObject *CSpace::takeIfExists(CObject *key, CObject *obj)
{
	obj = NULL;

        pthread_mutex_lock(&space_mutex);

	// ----------------------------------
	// return if there is no object 
	if(!space.empty()) {
		if(key==NULL) { // just read from front
			CObject &ref_obj = space.front(); // front() returns reference
			obj = ref_obj.clone();
			space.pop_front(); // erase the front object
		}
		else {
			for(int i=0;i<space.size();i++)  {
				if(*key==*(space[i])) { // real instance comparison
					CObject *found_obj = space[i];
					obj = found_obj->clone(); // return new allocated object 
					// erase the found object
					space.erase(space.begin()+i);
					break;
				}
			}
		}
	}
	// ----------------------------------

        pthread_mutex_unlock(&space_mutex);

        return obj;
}

//vector<CObject> &CSpace::take(CObject *key, vector<CObject> &objs)
vector< CSmartTemplate<CObject> > &CSpace::takeIfExists(CObject *key, size_t take_limit, vector< CSmartTemplate<CObject> > &objs)
{
	for(int limit=0;limit<take_limit;limit++) {
		CObject *obj = NULL;
		obj = takeIfExists(key, obj);
		if(obj==NULL) break;
		objs.push_back(*obj);
		delete obj;
	}

/*
        pthread_mutex_lock(&space_mutex);

	// ----------------------------------
	// return if there is no object 
	if(!space.empty()) {
		if(key==NULL) { // just read all objects
			//for(int i=0;i<space.size();i++)  objs.push_back(*(space[i]));
			for(int limit=0;limit<take_limit;limit++) {
				if(space.empty()) break;
				CObject &ref_obj = space.front(); // front() returns reference
				objs.push_back(ref_obj);
				space.pop_front(); // erase the front object
			}
		}
		else {
			int limit = 0;

			for(int i=0;i<space.size();i++)  {
				if(*key==*(space[i])) { // real instance comparison
					objs.push_back(space[i]);
					// erase the found object
					space.erase(space.begin()+i);

					limit++;
					if(limit>=take_limit) break;
				}
			}
		}
	}
	// ----------------------------------

        pthread_mutex_unlock(&space_mutex);
*/

        return objs;
}

int CSpace::size()
{
        pthread_mutex_lock(&space_mutex);

	int size = space.size();

        pthread_mutex_unlock(&space_mutex);
	
	return size;
}

#ifdef DEBUG
void CSpace::list()
{
        pthread_mutex_lock(&space_mutex);

	cout << "--------------- list ------------------" << endl;
	for(int i=0;i<space.size();i++) {
		CObject &obj= *(space[i]);
		obj.print();
	}
	cout << "---------------------------------------" << endl;
	cout << endl;

        pthread_mutex_unlock(&space_mutex);
}
#endif

int CSpace::purge()
{
	time_t current = time(NULL);
	
        pthread_mutex_lock(&space_mutex);

	if(!space.empty()) {
		deque< CSmartTemplate<CObject> >::iterator start = space.begin();
		while(start!=space.end()) {
			CObject &obj = *(start);
			if(obj.getTTL()<current) {
#ifdef DEBUG
				cout << "erasing object :";
				obj.print();
#endif
				start = space.erase(start);
				//start = space.begin();
			} 
			else start++;
		}
	}

        pthread_mutex_unlock(&space_mutex);
}

int CSpace::save()
{
// later
}

int CSpace::load()
{
// later
}
